#include "widget.h"
#include <QOpenGLShaderProgram>

// Front Verticies
#define VERTEX_FTR  0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f
#define VERTEX_FTL -0.5f,  0.5f,  0.5f, 0.0f, 1.0f, 0.0f
#define VERTEX_FBL -0.5f, -0.5f,  0.5f, 0.0f, 0.0f, 1.0f
#define VERTEX_FBR  0.5f, -0.5f,  0.5f, 0.0f, 0.0f, 0.0f

// Back Verticies
#define VERTEX_BTR  0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 0.0f
#define VERTEX_BTL -0.5f,  0.5f, -0.5f, 0.0f, 1.0f, 1.0f
#define VERTEX_BBL -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f
#define VERTEX_BBR  0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f

// Create a colored cube
static const GLfloat vertices[] =
{
    VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_BBR, VERTEX_BTL, VERTEX_BTR, VERTEX_BBL
};

static const GLuint indices[] =   // 注意索引从0开始!
{
    // Face 1 (Front)
    0, 1, 2,
    2, 3, 0,
    // Face 2 (Back)
    4, 5, 6,
    5, 4, 7,
    // Face 3 (Top)
    0, 6, 5,
    5, 1, 0,
    // Face 4 (Bottom)
    3, 2, 7,
    7, 4, 3,
    // Face 5 (Left)
    2, 1, 5,
    2, 5, 7,
    // Face 6 (Right)
    0, 3, 4,
    4, 6, 0
};

#undef VERTEX_BBR
#undef VERTEX_BBL
#undef VERTEX_BTL
#undef VERTEX_BTR

#undef VERTEX_FBR
#undef VERTEX_FBL
#undef VERTEX_FTL
#undef VERTEX_FTR

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent), vbo(QOpenGLBuffer::VertexBuffer), ebo(QOpenGLBuffer::IndexBuffer)
{
    program = Q_NULLPTR;
    m_transform.translate(0.0f, 0.0f, -5.0f); // moved back 5 units
}

Widget::~Widget()
{
    makeCurrent();
    teardownGL();
}

void Widget::initializeGL()
{
    // Initialize OpenGL Backend
    initializeOpenGLFunctions();
    connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); // it’s more common in modern graphics programming to schedule a redraw immediately after VSync, and by default Qt5 starts up with VSync enabled.
    printContextInformation();

    // Set global information
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glEnable(GL_CULL_FACE);
//    glEnable(GL_DEPTH_TEST); // vbo+vao或者ebo+vbo+vao时，加上这一句就不会输入任何内容？？

    //
    program = new QOpenGLShaderProgram();
    program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/simple.vert");
    program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/simple.frag");
    program->link();
    program->bind();

    // Cache Uniform Locations
    u_modelToWorld = program->uniformLocation("modelToWorld");
    u_worldToView = program->uniformLocation("worldToView");
//    u_viewToClip = program->uniformLocation("viewToClip");

    // Create Vertex Array Object
    vao.create();
    vao.bind();

    // Create Buffer (Do not release until VAO is created)
    vbo.create();
    vbo.bind();
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    vbo.allocate(vertices, sizeof(vertices));

    ebo.create();
    ebo.bind();
    ebo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    ebo.allocate(indices, sizeof(indices));

    //
    program->enableAttributeArray(0);
    program->setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 6);

    program->enableAttributeArray(1);
    program->setAttributeBuffer(1, GL_FLOAT, sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);

    // Release (unbind) all
    vbo.release();
    vao.release();
    program->release();
}

void Widget::resizeGL(int width, int height)
{
    m_projection2.setToIdentity(); // 是projction matrix吗？

    m_projection.setToIdentity();
    m_projection.perspective(45.0f, width / float(height), 0.0f, 1000.0f); // The perspective matrix is our CameraToView matrix （Perspective Matrix or a View Matrix）

//    it should be avoided to update any OpenGL state in the resizeGL() function
}

void Widget::paintGL()
{
    // Clear
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Render using our shader
    program->bind();
//    program->setUniformValue(u_viewToClip, m_projection2);
    program->setUniformValue(u_worldToView, m_projection); // 不需要每帧更新worldToView，为简单起见，我们在此处更新。是view matrix吗？

    vao.bind();
    program->setUniformValue(u_modelToWorld, m_transform.toMatrix()); // 是model matrix吗？
//    glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices) / sizeof(vertices[0]));
    glDrawElements(GL_TRIANGLES, ebo.size() / sizeof(GLuint), GL_UNSIGNED_INT, Q_NULLPTR);
    vao.release();

    program->release();
}

void Widget::update()
{
    // Update instance information
    m_transform.rotate(1.0f, QVector3D(0.4f, 0.3f, 0.3f)); // rotate by 1.0f degrees every call

    // Schedule a redraw
    QOpenGLWidget::update();
}

void Widget::teardownGL()
{
    ebo.destroy();
    vbo.destroy();
    vao.destroy();
    delete program;
}

void Widget::printContextInformation()
{
    QString glType;
    QString glVersion;
    QString glProfile;

    // Get Version Information
    glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
    glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));

    // Get Profile Information
#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break
    switch (format().profile())
    {
            CASE(NoProfile);
            CASE(CoreProfile);
            CASE(CompatibilityProfile);
    }
#undef CASE

    // qPrintable() will print our QString w/o quotes around it.
    qDebug() << qPrintable(glType) << qPrintable(glVersion) << "(" << qPrintable(glProfile) << ")";
}
